var rewire = require("rewire")
var markdownpdf = require("markdown-pdf")
var cheerio = require('cheerio')
const R = require('ramda')
const {YsError} = rewire('../common/exception.cjs')

async function markdownToPdf(mdstr, filePath) {
  const p = new Promise(function(resolve,reject){
    markdownpdf().from.string(mdstr).to(filePath, function () {
      resolve(filePath)
    })
  })
  return await p
}





function htmlToMarkDown(html) {
  const pack = composePackOr(weiboPack, zhihuPack)
  const f = makeParser(pack)
  return f(html)
}

// isMatch(node, env) 检查一个节点，如果通过则调用converter。
// parse(node, env, mdParser) 将节点转换成md字符串，env是转换环境，用来保存临时变量
function makeDomHandler(isMatch, parse) {
  {isMatch, parse}
}

// 创建特性包
function createPack(checker, ...domHandlers) {
  return {
    check: checker,
    handle: makeNodesHandler(domHandlers),
    getDomHandlers: function() { return domHandlers }
  }
}


// 将解析包组合成一个大的组合包
function composePackAnd(...packList) {
  return createPack(
    function(html, env) {
      for (let pack of packList) {
        if (pack.check(html, env)) {
          return true
        }
      }
      return false
    },
    [].concat(packList.map(item => item.getDomHandlers()))
  )
}

// 将解析包组合成一个大的组合包
function composePackOr(...packList) {
  let fpack = null
  return {
    check: function(html, env) {
      for (let pack of packList) {
        if (pack.check(html, env)) {
          fpack = pack
          return true
        }
      }
      return false
    },
    handle: function(nodes, env) {
      const f = makeNodesHandler(fpack.getDomHandlers())
      return f(nodes, env)
    },
    getDomHandlers: function() {
      return fpack.getDomHandlers()
    }
  }
}

function makeParser(pack) {
  return function(html) {
    let env = {}
    const valid = pack.check(html, env)
    if (valid) {
      const nodes = cheerio.parseHTML(html)
      return pack.handle(nodes, env)
    }
    else {
      return defaultPack.handle(nodes, env)
    }
  }
}

function makeNodesHandler(domHandlers) {
  function parser(node, env) {
    const ls = R.concat(
      [
        makeDomHandler(
          function(node, env)
          {
            return Array.isArray(node)
          },
          function(node, env, p) {
            const rs = R.map(function(item) {
              return parser(item, env)
            }, node).join('')
            return rs
          }
        ),
        makeDomHandler(
          function(node, env)
          {
            return node.type == 'text'
          },
          function(node, env, p) {
            return node.data
          }
        )
      ],
      domHandlers
    )
    for (let pie of ls) {
      if (pie.isMatch(node, env)) {
        return pie.parse(node, env, parser)
      }
    }
    throw new YsError('不支持该node', 'makeNodesHandler', node)
  }
}












function nodeChildren(node) {
  if (node.type == 'tag') {
    return node.children
  }
  else {
    console.error(node)
    throw new Error(`该节点没有子节点`)
  }
}


function hasClass(node, className) {
  return node.attribs['class'] && R.includes(className, node.attribs['class'].split(' '))
}

function nodeText(node) {
  if (node.type == 'text' && node.data != ' ') {
    return node.data
  }
  else {
    return node.children.map(child => nodeText(child)).join('')
  }
}






let aDom = {
  isMatch: function(node, env)
  {
    return node.name == 'a'
  },
  parse: function(node, env, mdParser) {
    const cnode = nodeChildren(node)
    const href = node.attribs['href']
    return `[${mdParser(cnode, env).trim()}](${href})`
  }
}

let ulDom = {
  isMatch: function(node, env)
  {
    return node.name == 'ul'
  },
  parse: function(node, env, mdParser) {
    env.level = (env.level ?? 0) + 1
    const rs = nodeChildren(node)
          .filter(c => { return c.type == 'tag' })
          .map(function(elem) {
            return R.repeat(`\t`, env.level - 1).join('')
              + '- '
              + mdParser(nodeChildren(elem), env)
          })
          .join('\n')
    env.level = env.level - 1
    return `\n${rs}\n`
  }
}

let olDom = {
  isMatch: function(node, env)
  {
    return node.name == 'ol'
  },
  parse: function(node, env, mdParser) {
    env.level = (env.level ?? 0) + 1
    const rs = nodeChildren(node)
          .filter(c => { return c.type == 'tag' })
          .map(function(elem, i) {
            return R.repeat(`\t`, env.level - 1).join('') + `${i + 1} ` + mdParser(nodeChildren(elem), env)
          }).join('\n')
    env.level = env.level - 1
    return `\n${rs}\n`
  }
}



let hDom = {
  isMatch: function(node, env)
  {
    const reg = new RegExp('h\\d')
    return reg.test(node.name)
  },
  parse: function(node, env, mdParser) {
    const cont = parseInt(R.drop(1, node.name))
    return '\n' + R.repeat('#', cont).join('') + " " + mdParser(nodeChildren(node), env) + '\n'
  }
}

let imgDom = {
  isMatch: function(node, env)
  {
    return node.name == 'img'
  },
  parse: function(node, env, mdParser) {
    const src = node.attribs['src']
    const alt = node.attribs['alt']
    return `\n\n![${alt}](${src})\n\n`
  }
}

let brDom = {
  isMatch: function(node, env)
  {
    return node.tagName == 'br'
  },
  parse: function(node, env, mdParser) {
    return '\n\n'
  }
}



let stongDom = {
  isMatch: function(node, env)
  {
    return node.name == 'strong'
  },
  parse: function(node, env, mdParser) {
    return `**${mdParser(nodeChildren(node))}**`
  }
}



let quoteDom = {
  isMatch: function(node, env)
  {
    return node.name == 'span' && hasClass(node, 'node')
  },
  parse: function(node, env, mdParser) {
    env.qLevel = (env.qLevel ?? 0) + 1
    let rs =  R.repeat(`>`, env.qLevel).join('') + ` ${mdParser(nodeChildren(node))} \n`
    env.qLevel = env.qLevel - 1
    if (env.qLevel == 0) {
      rs = "\n" + rs + "\n"
    }
    return rs
  }
}

let hrDom = {
  isMatch: function(node, env)
  {
    return R.contains(node.name, ['hr'])
  },
  parse: function(node, env, mdParser) {
    return `\n\n---\n\n`
  }
}

let blockDom = {
  isMatch: function(node, env)
  {
    return R.contains(node.name, ['div', 'p', 'pre'])
  },
  parse: function(node, env, mdParser) {
    const rs = mdParser(nodeChildren(node), env)
    return `\n\n${rs}\n\n`
  }
}

let inlineDom = {
  isMatch: function(node, env)
  {
    return R.contains(node.name, ['span'])
  },
  parse: function(node, env, mdParser) {
    const rs = mdParser(nodeChildren(node), env)
    return `${rs}`
  }
}

let ignoreDom = {
  isMatch: function(node, env)
  {
    return R.contains(node.name, ['button'])
  },
  parse: function(node, env, mdParser) {
    return ''
  }
}


let codeLineDom = {
  isMatch: function(node, env)
  {
    return (node.name == 'code' && !hasClass(node, 'code-highlight'))
  },
  parse: function(node, env, mdParser) {
    const rs = mdParser(nodeChildren(node), env).trim()
    return `\`${rs}\``
  }
}



let codeBlockDom = {
  isMatch: function(node, env)
  {
    return (node.name == 'code' && hasClass(node, 'code-highlight'))
  },
  parse: function(node, env, mdParser) {
    return `\n\`\`\`\n${nodeText(node)}\n\`\`\`\n`
  }
}




const defaultPack = createPack(
  function(html) { return true },
  [
    aDom, ulDom, olDom, hDom, imgDom, brDom,
    codeLineDom, codeBlockDom, stongDom, quoteDom,
    hrDom, blockDom, inlineDom, ignoreDom
  ]
)

/// 还有很多平台包没弄呢。


module.exports = {markdownToPdf, htmlToMarkdown, makeToMarkdowndParser}
